package com.ms.service.module.datacenterlite;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

import com.ms.game.common.GameType;
import com.ms.game.gmtool.message.SGmtoolChangeGameModuleState;
import com.ms.service.module.common.timer.UpdateTaskTimer2;
import com.ms.service.telnet.STelnetCmd;
import com.xpec.c4.db.dao.PlayerKeepSeatDAO;
import com.xpec.c4.db.dao.PlayerKeepSeatLogDAO;
import com.xpec.c4.db.model.PlayerKeepSeatClearLogModel;
import com.xpec.c4.db.model.PlayerKeepSeatLogModel;
import com.xpec.c4.db.model.PlayerKeepSeatModel;
import com.xpec.c4.game.time.TimeConstants;
import com.xpec.c4.game.time.TimeSys;
import com.xpec.c4.game.utility.GameUtility;
import com.xpec.c4.message.MessageTypes;
import com.xpec.c4.service.group.transportlayer.GroupMessage;
import com.xpec.c4.service.message.MessageAction;
import com.xpec.c4.service.module.BasicServiceModule;
import com.xpec.c4.service.module.ModuleName;
import com.xpec.c4.service.module.ModuleState;
import com.xpec.c4.service.module.task.TaskAsyncCommand;
import com.xpec.c4.service.module.task.TaskTimer;
public class DataCenterLiteModule extends BasicServiceModule {
	/** 處理清除保留座位的時間間隔 */
	public static final long CLEAR_KEEP_SEAT_TIMER_INTERVAL = 20 * TimeConstants.MILLI_SECOND_PRE_SECOND;

	private static final Log log = LogFactory.getLog(DataCenterLiteModule.class);
	
	private static DataCenterLiteModule instance;

	/**
	 * 給座位保留特別處理的 queue id
	 */
	public int asyncQueueIdForKeepSeatData = asyncTaskQueueCount -1;

	/** 是否正在執行清除座位保留 */
	private boolean keepSeatClearExecute = false;
	
	/**玩家保留機台快取**/
	private static ConcurrentHashMap<String , GameKeepSeatAllData> keepSeat = 	new ConcurrentHashMap<String , GameKeepSeatAllData>();
	
	public static ConcurrentHashMap<String, GameKeepSeatAllData> getKeepSeat() {
		return keepSeat;
	}

	public static void setKeepSeat(
			ConcurrentHashMap<String, GameKeepSeatAllData> keepSeat) {
		DataCenterLiteModule.keepSeat = keepSeat;
	}
	
	/**正在運行中狀態的遊戲*/
	private Set<String> runningGameList = new HashSet<String>();
	
	@Override
	public boolean startup() 
	{
		//撈取所有保留資料並快取
		onSelectAllKeepData();
		
		return super.startup();
	}
	
	@Override
	public void onServerConnectReady() {
		super.onServerConnectReady();
		
		//檢查玩家保留超時
		GameKeepDataTimeoutTask keeptask = new GameKeepDataTimeoutTask();
		this.addTaskTimer(keeptask, TaskTimer.LOOP,
				GameKeepDataTimeoutTask.TASK_DELAYTIME,
				GameKeepDataTimeoutTask.TASK_DELAYTIME, this);

		UpdateTaskTimer2 keepSeatClearTimerTask = new UpdateTaskTimer2(this, "onKeepSeatClearTimerTaskExecute");
		addTaskTimer(keepSeatClearTimerTask, TaskTimer.LOOP, CLEAR_KEEP_SEAT_TIMER_INTERVAL,
				CLEAR_KEEP_SEAT_TIMER_INTERVAL, this);
	}

	@Override
	public void shutdown() {
		super.shutdown();
	}

	@Override
	public void init() {
		super.init();
		
		instance = this;
		
		registerActionClass(TransactionAction.class);
		registerActionClass(DataCenterLiteAction.class);
		
		//如果自己一開機就是runningState就直接加
		if(this.isRunning()){
			addAllRunningGames();
		}
	}
	
	@Override
	public boolean addAsyncCommand(TaskAsyncCommand obj) {
		
		if(obj == null){
			return false;
		}

		//判斷是否超過預設 threadID, 取正值
		int key = obj.getQueueID();
		if(key != 0){
			int hash = key % asyncTaskQueueCount-2;
			obj.setQueueID((hash < 0) ? -hash : hash);
		}
		
		//保留特別處理
		if(obj instanceof CheckKeepDataTimeOut)
		{
			obj.setQueueID(asyncQueueIdForKeepSeatData);
		}
		
		
		
		
		return super.addAsyncCommand(obj);
	}

	@Override
	public void procTelnetCommand(STelnetCmd obj) {
		// TODO Auto-generated method stub

	}
	
	/**
	 * 檢測玩家保留超時狀況
	 */
	@SuppressWarnings("unchecked")
	public void onCheckKeepDataTimeout()
	{
		// 放超時要被清除的座位資料，寫入 db 後由另一個 timer 定時處理
		ArrayList<PlayerKeepSeatClearLogModel> seatClearLogList = new ArrayList<PlayerKeepSeatClearLogModel>();

		long nowtime = TimeSys.currentTimeMills();
		for (Map.Entry<String, GameKeepSeatAllData> entry : keepSeat.entrySet()) {
			String guid = (String) entry.getKey();
			GameKeepSeatAllData allData = entry.getValue();
			
			// 同步鎖
			synchronized (allData) {
				ArrayList<GameKeepSeatData> value = allData.getDataList();

				// 掃一遍角色底下的座位保留資料
				boolean seatListChange = false;
				Iterator<GameKeepSeatData> it = value.iterator();
				while (it.hasNext()) {
					GameKeepSeatData dd = it.next();
					
					//不在運行中的遊戲不處理
					if(!runningGameList.contains(dd.getGameName()))
						continue;

					// 發現過期
					if (nowtime > dd.getTimeout()) {
						// 做log
						PlayerKeepSeatLogModel seatlog = new PlayerKeepSeatLogModel();
						seatlog.setStat(PlayerKeepSeatLogModel.TIMEOUT);
						seatlog.setGamename(dd.getGameName());
						seatlog.setHalltype(dd.getHalltype());
						seatlog.setSeatid(dd.getSeatid());
						seatlog.setMachineid(dd.getMachineid());
						seatlog.setGuid((String) entry.getKey());
						seatlog.setTimeout(dd.getTimeout());
						seatlog.setIsofflinekeep(dd.isIsofflinekeep());
						seatlog.setCreatetime(TimeSys.currentTimeMills());
						PlayerKeepSeatLogDAO.addPlayerKeepLog(seatlog);

						// 產生等待清除的座位資料
						PlayerKeepSeatClearLogModel cl = new PlayerKeepSeatClearLogModel();
						cl.setGameType(dd.getGameName());
						cl.setHallType(dd.getHalltype());
						cl.setSeatId(dd.getSeatid());
						cl.setMachineId(dd.getMachineid());
						cl.setPlayerId(guid);
						cl.setOfflineKeep(dd.isIsofflinekeep());
						cl.setTimeOut(GameUtility.timeToCalendar(dd.getTimeout()));
						cl.setMachineStat(dd.getMachinestat());
						cl.setModuleId(dd.getModuleid());
						cl.setStatus(PlayerKeepSeatClearLogModel.WAIT_CLEAR);
						seatClearLogList.add(cl);

						if (log.isInfoEnabled()) {
							log.info("<<onCheckKeepDataTimeout>> time out, push to PlayerKeepSeatClearLogModel !! seatID : "
									+ dd.getSeatid()
									+ "  MachineID : "
									+ dd.getMachineid()
									+ "  Playerid : "
									+ (String) entry.getKey());
						}

						// 刪除過期的
						it.remove();
						
						// 標記資料有變更
						seatListChange = true;
					} // if (nowtime > dd.getTimeout()) {
				} // while (it.hasNext()) {

				if (seatListChange) {
					JSONObject json = new JSONObject();
					JSONArray list = new JSONArray();
					for (GameKeepSeatData ss : allData.getDataList()) {
						JSONObject obj = ss.toJson();
						list.add(obj);
					}

					json.put("keep", list);

					// 修改 data model
					PlayerKeepSeatModel model = allData.getDataModel();
					model.setContent(json.toJSONString());
					model.setModifytime(TimeSys.currentTimeMills());

					// 更新回 db
					PlayerKeepSeatDAO.modifyKeepSeatData(model);
				} // if (seatListChange) {
			} // synchronized (value) {
		} // for (Map.Entry<String, GameKeepSeatAllData> entry : keepSeat.entrySet()) {

		// 將清單內容寫入 db
		PlayerKeepSeatDAO.saveWaitClearKeepSeatData(seatClearLogList);
	}
	
	/**
	 * 定時從待處理清單中清除座位保留資料
	 */
	public void onKeepSeatClearTimerTaskExecute() {
		if (!keepSeatClearExecute) {
			KeepSeatClearAsync task = new KeepSeatClearAsync();
			task.setSrcModule(this);
			addAsyncCommand(task);

			keepSeatClearExecute = true;
		}
	}

	/**
	 * 座位保留資料清除完畢
	 */
	public void onKeepSeatClearTimerTaskFinish() {
		keepSeatClearExecute = false;
	}

	/**
	 * 撈取所有保留資料
	 */
	private static void onSelectAllKeepData()
	{
		//等新增玩家流水號後，每個dc會有自己的範圍
		GameGetKeepSeatAyncCommand keep = new GameGetKeepSeatAyncCommand();
		
		List<PlayerKeepSeatModel>  list = PlayerKeepSeatDAO.getKeepData(0, 0);
		for(PlayerKeepSeatModel data:list)
		{
			ArrayList<GameKeepSeatData>  seats = keep.parseJson(data.getContent(), data.getDb_id());
			
			GameKeepSeatAllData allData = new GameKeepSeatAllData();
			allData.setDataList(seats);
			allData.setDataModel(data);
			
			keepSeat.put(data.getGuid(), allData);
		}
	}

	@Override
	public void procThrowException(Object obj) {
		// TODO Auto-generated method stub
		
	}

	public static DataCenterLiteModule getInstance() {
		return instance;
	}
	
	
	/**
	 * 延長所有玩家在所有機台保留座位的時間
	 * @param extendTime
	 */
	@SuppressWarnings("unchecked")
	public void extendPlayersKeepSeatTime(ArrayList<String> targetGames, long extendTime){
		//允許DCLite本身在運作中執行，會去判斷該遊戲Module目前運行狀態是運行中就跳過
		
		//現在時間
		long currentTime = TimeSys.currentTimeMills();
		//新的過期時間(現在時間+設定要延長的時間)
		long newTimeOut = currentTime + extendTime;
		
		//對每個玩家的allData(這裡不需要用到key，所以不用entry去loop)
		for(GameKeepSeatAllData allData : keepSeat.values()){
			ArrayList<GameKeepSeatData> keepDatas = allData.getDataList();
			if(keepDatas != null){
				//這個玩家保留座位有沒有被改變的標記
				boolean isChanged = false;
				//新的資料
				JSONArray jArray = new JSONArray();
				for(GameKeepSeatData keepData : keepDatas){
					//如果沒設定目標遊戲，或者有設定時與保存紀錄相符時才進去處理
					if(targetGames == null || targetGames.size() == 0 || targetGames.contains(keepData.getGameName())){
						//如果該遊戲在運行狀態時，不操作
						if(runningGameList.contains(keepData.getGameName()))
							continue;
						
						//只要現在距離保留座位時間小於設定時間
						//2018-01-22 企劃說調整成齊頭式的保留時間(不管大於還是小於，通通變成這個時間)
//						if(keepData.getTimeout() - currentTime < extendTime){
							//延長時間
							keepData.setTimeout(newTimeOut);
							
							JSONObject jObj = keepData.toJson();
							jArray.add(jObj);
							
							isChanged = true;
//						}
					}
				}
				
				//如果有被改變就寫DB
				if(isChanged){
					JSONObject jObjFORDB = new JSONObject();
					jObjFORDB.put("keep", jArray);
					//延長MODEL的時間
					allData.getDataModel().setContent(jObjFORDB.toJSONString());
					allData.getDataModel().setModifytime(currentTime);
					
					PlayerKeepSeatDAO.modifyKeepSeatData(allData.getDataModel());
				}
			}
		}
	}
	
	
	/**
	 * 送遊戲module狀態改變給DataCenterLite
	 * @param gameTypes
	 * @param moduleState
	 */
	public static void sendGameModuleStateChangedToDCLite(String[] gameTypes, int moduleState){
		ArrayList<String> gameList = null;
		if(gameTypes != null)
			 gameList = new ArrayList<String>(Arrays.asList(gameTypes));
			 
		SGmtoolChangeGameModuleState message = new SGmtoolChangeGameModuleState();
		message.setChangeGameList(gameList);
		message.setModuleState(moduleState);
		
		GroupMessage msg = new GroupMessage();
		msg.setDestModuleID(ModuleName.DATA_CENTER_LITE);
		msg.setMessageType(MessageTypes.S_GMTOOL_CHANGE_GAME_MODULE_STATE_TO_DCLITE);
		msg.setMessageContent(message);
		MessageAction.sendMessage(msg);
	}
	
	/**
	 * 收到GMTOOL改變遊戲Module狀態<br>
	 * (目前只有處理是否是running模式)
	 * @param message
	 */
	public void recGMToolChangeGameModuleState(SGmtoolChangeGameModuleState message){
		System.out.println("recGMToolChangeGameModuleState, ModuleState:"+message.getModuleState()+"\nfollowing gameTypes:");
		for(String gameType : message.getChangeGameList()){
			System.out.println(gameType);
		}
		
		//是否是運作狀態
		boolean isRunningState = false;
		if(message.getModuleState() == ModuleState.RUNNING.getLevel())
			isRunningState = true;
		
		//如果沒指定遊戲，就是全部
		if(message.getChangeGameList() == null || message.getChangeGameList().size() == 0){
			if(isRunningState){
				//加全部
				addAllRunningGames();
			}
			else{
				//清除全部
				runningGameList.clear();
			}
		}
		else{
			for(String gameType : message.getChangeGameList()){
				if(isRunningState){
					runningGameList.add(gameType);
				}
				else{
					if(runningGameList.contains(gameType)){
						runningGameList.remove(gameType);
					}
				}
			}
		}
	}
	
	/**
	 * 增加全部遊戲到runningGameList
	 */
	public void addAllRunningGames(){
		for(GameType gameType : GameType.values()){
			runningGameList.add(gameType.name());
		}
	}
	
}
